﻿using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.EventSystems;

namespace SubmeshManager
{
    public class SubmeshController : MonoBehaviour
    {
        /// <summary>
        /// 拆分出submesh
        /// </summary>
        /// <param name="submeshIndex"></param>
        /// <param name="good"></param>
        /// <returns></returns>
        private static GameObject SplitSubmesh(int submeshIndex, Transform good)
        {
            MeshRenderer meshRender = good.GetComponent<MeshRenderer>();
            MeshFilter meshFilter = good.GetComponent<MeshFilter>();

            Mesh submesh = GetSubmesh(meshFilter.mesh, submeshIndex);
            submesh.RecalculateBounds();
            Material subMaterial = meshRender.materials[submeshIndex];

            GameObject newObj = new GameObject();
            newObj.transform.position = good.position;
            newObj.transform.rotation = good.rotation;
            newObj.transform.localScale = good.localScale;
            newObj.AddComponent<MeshFilter>().sharedMesh = submesh;
            newObj.AddComponent<MeshRenderer>().material = subMaterial;

            return newObj;
        }

        /// <summary>
        /// 剔除已合并的Submesh
        /// </summary>
        /// <returns></returns>
        private static GameObject ExcludeSubmesh(int submeshIndex, Transform good)
        {
            MeshRenderer meshRender = good.GetComponent<MeshRenderer>();
            MeshFilter meshFilter = good.GetComponent<MeshFilter>();

            GameObject newObj = new GameObject(good.gameObject.name);
            List<CombineInstance> listCombines = new List<CombineInstance>();
            List<Material> subMaterials = new List<Material>();
            
            int newDicIndex = 0;
            for (int i = 0; i < meshRender.materials.Length; ++i)
            {
                if (i != submeshIndex)
                {
                    Mesh submesh = GetSubmesh(meshFilter.mesh, i);
                    CombineInstance ins = new CombineInstance();
                    ins.mesh = submesh;
                    ins.transform = good.localToWorldMatrix;
                    listCombines.Add(ins);
                    subMaterials.Add(good.GetComponent<MeshRenderer>().materials[i]);

                    newDicIndex++;
                }
            }

            Mesh result = new Mesh();
            result.Clear();
            result.CombineMeshes(listCombines.ToArray(), false);
            result.RecalculateBounds();

            Material[] mats = new Material[subMaterials.Count];
            for (int i = 0; i < subMaterials.Count; ++i)
            {
                mats[i] = subMaterials[i];
            }

            newObj.AddComponent<MeshFilter>().sharedMesh = result;
            newObj.AddComponent<MeshRenderer>().materials = mats;
            MakeSurePivotIsCenter(newObj);

            return newObj;
        }

        //遍历subMesh的triangle，加入到vertex中，返回索引，最终设置新Mesh的vertex和triangle即可
        public static Mesh GetSubmesh(Mesh aMesh, int aSubMeshIndex)
        {
            if (aSubMeshIndex < 0 || aSubMeshIndex >= aMesh.subMeshCount)
                return null;
            int[] indices = aMesh.GetTriangles(aSubMeshIndex);
            Vertices source = new Vertices(aMesh);
            Vertices dest = new Vertices();
            Dictionary<int, int> map = new Dictionary<int, int>();
            int[] newIndices = new int[indices.Length];
            for (int i = 0; i < indices.Length; i++)
            {
                int o = indices[i];
                int n;
                if (!map.TryGetValue(o, out n))
                {
                    n = dest.Add(source, o);
                    map.Add(o, n);
                }
                newIndices[i] = n;
            }
            Mesh m = new Mesh();
            dest.AssignTo(m);
            m.triangles = newIndices;
            return m;
        }

        //新生成的物体确定重心为中心
        public static void MakeSurePivotIsCenter(GameObject obj)
        {
            List<Mesh> meshs = new List<Mesh>();
            List<MeshFilter> meshFilters = obj.GetComponents<MeshFilter>().ToList();
            foreach (MeshFilter meshFilter in meshFilters)
            {
                meshs.Add(meshFilter.sharedMesh);
            }

            for (int i = 0; i < meshs.Count; i++)
            {
                var vers = meshs[i].vertices.Select(x => obj.transform.TransformPoint(x)).ToArray();
                if (vers.Length == 0)
                {
                    Debug.LogError("无顶点");
                    continue;
                }
                Vector3 min, max;
                min = max = vers[0];
                for (int j = 1; j < vers.Length; j++)
                {
                    Vector3 V = vers[j];
                    for (int n = 0; n < 3; n++)
                    {
                        if (V[n] > max[n])
                            max[n] = V[n];
                        if (V[n] < min[n])
                            min[n] = V[n];
                    }
                }
                Bounds B = new Bounds();
                B.SetMinMax(min, max);

                obj.transform.position = new Vector3(B.center.x, B.center.y - B.extents.y, B.center.z);
                meshs[i].vertices = vers.Select(x => obj.transform.InverseTransformPoint(x)).ToArray();
                meshs[i].RecalculateBounds();

                if (obj.GetComponent<MeshCollider>() != null)
                {
                    obj.GetComponent<MeshCollider>().sharedMesh = meshs[i];
                }
            }
        }
    }

    class Vertices
    {
        List<Vector3> verts = null;
        List<Vector2> uv1 = null;
        List<Vector2> uv2 = null;
        List<Vector2> uv3 = null;
        List<Vector2> uv4 = null;
        List<Vector3> normals = null;
        List<Vector4> tangents = null;
        List<Color32> colors = null;
        List<BoneWeight> boneWeights = null;

        public Vertices()
        {
            verts = new List<Vector3>();
        }
        public Vertices(Mesh aMesh)
        {
            verts = CreateList(aMesh.vertices);
            uv1 = CreateList(aMesh.uv);
            uv2 = CreateList(aMesh.uv2);
            uv3 = CreateList(aMesh.uv3);
            uv4 = CreateList(aMesh.uv4);
            normals = CreateList(aMesh.normals);
            tangents = CreateList(aMesh.tangents);
            colors = CreateList(aMesh.colors32);
            boneWeights = CreateList(aMesh.boneWeights);
        }

        private List<T> CreateList<T>(T[] aSource)
        {
            if (aSource == null || aSource.Length == 0)
                return null;
            return new List<T>(aSource);
        }
        private void Copy<T>(ref List<T> aDest, List<T> aSource, int aIndex)
        {
            if (aSource == null)
                return;
            if (aDest == null)
                aDest = new List<T>();
            aDest.Add(aSource[aIndex]);
        }
        public int Add(Vertices aOther, int aIndex)
        {
            int i = verts.Count;
            Copy(ref verts, aOther.verts, aIndex); //verts存储顶点位置
            Copy(ref uv1, aOther.uv1, aIndex);   //uv1存储每个顶点的uv
            Copy(ref uv2, aOther.uv2, aIndex);
            Copy(ref uv3, aOther.uv3, aIndex);
            Copy(ref uv4, aOther.uv4, aIndex);
            Copy(ref normals, aOther.normals, aIndex);
            Copy(ref tangents, aOther.tangents, aIndex);
            Copy(ref colors, aOther.colors, aIndex);
            Copy(ref boneWeights, aOther.boneWeights, aIndex);
            return i;
        }
        public void AssignTo(Mesh aTarget)
        {
            if (verts.Count > 65535)
                aTarget.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
            aTarget.SetVertices(verts);
            if (uv1 != null) aTarget.SetUVs(0, uv1);
            if (uv2 != null) aTarget.SetUVs(1, uv2);
            if (uv3 != null) aTarget.SetUVs(2, uv3);
            if (uv4 != null) aTarget.SetUVs(3, uv4);
            if (normals != null) aTarget.SetNormals(normals);
            if (tangents != null) aTarget.SetTangents(tangents);
            if (colors != null) aTarget.SetColors(colors);
            if (boneWeights != null) aTarget.boneWeights = boneWeights.ToArray();
        }
    }
}

